﻿using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Transactions;
using System.Xml;
using BookmarksDb.Data;

namespace ImportBookmarksFromXml
{
    public class BookmarksImporter
    {
        public static void Main()
        {
            string filePath = @"../../../../Resources/bookmarks.xml";

            Console.WriteLine("Importing bookmarks from XML document ...");
            Import(filePath);
            Console.WriteLine("Bookmarks imported successfully!");
        }

        private static void Import(string filePath)
        {
            XmlReader reader = XmlReader.Create(filePath);

            using (reader)
            {
                string username = string.Empty;
                string title = string.Empty;
                string url = string.Empty;
                string notes = string.Empty;
                string allTags = string.Empty;
                string[] tags = new string[] { };

                using (var context = new BookmarksDBEntities())
                {
                    var transaction = new TransactionScope(TransactionScopeOption.Required,
                    new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead });

                    using (transaction)
                    {
                        bool bookmarkRead = false;

                        while (reader.Read())
                        {
                            if (reader.Name == "bookmark" && bookmarkRead)
                            {
                                CheckRequiredElementsExistance(username, title, url);

                                AddBookmark(context, username, title, url, notes, tags);

                                ResetValues(ref username, ref title, ref url, ref notes, ref tags);

                                bookmarkRead = false;
                            }
                            else if (reader.Name == "username")
                            {
                                username = reader.ReadElementString();
                                bookmarkRead = true;
                            }
                            else if (reader.Name == "title")
                            {
                                title = reader.ReadElementString();
                                bookmarkRead = true;
                            }
                            else if (reader.Name == "url")
                            {
                                url = reader.ReadElementString();
                                bookmarkRead = true;
                            }
                            else if (reader.Name == "notes")
                            {
                                notes = reader.ReadElementString();
                                bookmarkRead = true;
                            }
                            else if (reader.Name == "tags")
                            {
                                allTags = reader.ReadElementString();
                                tags = allTags.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                int tagsCount = tags.Length;

                                for (int i = 0; i < tagsCount; i++)
                                {
                                    tags[i] = tags[i].ToLower().Trim();
                                }

                                bookmarkRead = true;
                            }
                        }

                        context.SaveChanges();
                        transaction.Complete();
                    }
                }
            }
        }

        private static void CheckRequiredElementsExistance(string username, string title, string url)
        {
            if (username == string.Empty || title == string.Empty || url == string.Empty)
            {
                throw new ArgumentException("Missing required element (username, title or url)!");
            }
        }

        private static void AddBookmark(BookmarksDBEntities context, string username, string title, string url, string notes, string[] tags)
        {
            var transaction = new TransactionScope(TransactionScopeOption.Required,
                new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead });

            using (transaction)
            {
                Bookmark newBookmark = new Bookmark();
                newBookmark.User = CreateOrLoadUser(context, username);
                newBookmark.Title = title;
                newBookmark.URL = url;
                newBookmark.Notes = notes;

                foreach (var tag in tags)
                {
                    Tag currentTag = CreateOrLoadTag(context, tag);
                    newBookmark.Tags.Add(currentTag);
                }

                context.Bookmarks.Add(newBookmark);
                context.SaveChanges();

                string[] titleTags = Regex.Split(title, @"[,'!\. ;?-]+");
                foreach (var tag in titleTags)
                {
                    Tag currentTag = CreateOrLoadTag(context, tag.ToLower());
                    newBookmark.Tags.Add(currentTag);
                }

                context.SaveChanges();
                transaction.Complete();
            }
        }

        private static User CreateOrLoadUser(BookmarksDBEntities context, string username)
        {
            var existingUser = context.Users.Where(x => x.Username == username).FirstOrDefault();

            if (existingUser != null)
            {
                return existingUser;
            }

            User newUser = new User();
            newUser.Username = username;

            return newUser;
        }

        private static Tag CreateOrLoadTag(BookmarksDBEntities context, string tagName)
        {
            var existingTag = context.Tags.Where(x => x.Name == tagName).FirstOrDefault();

            if (existingTag != null)
            {
                return existingTag;
            }

            Tag newTag = new Tag();
            newTag.Name = tagName;

            return newTag;
        }

        private static void ResetValues(ref string username, ref string title, ref string url, ref string notes, ref string[] tags)
        {
            username = string.Empty;
            title = string.Empty;
            url = string.Empty;
            notes = string.Empty;
            tags = new string[] { };
        }
    }
}
